﻿using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
using MassIntellect;
using System.Text.RegularExpressions;
using System.Data.SqlTypes;
using System.Globalization;
using AIS2.DataAccess;
using System.Transactions;
//using MassIntellect.WorkIt;

//using ErrorManager = System.Collections.Generic.Dictionary<System.Int32, AIS2.Error>;

namespace AIS2
{
	public class AISReportLoadSession : IAISReportLoadSession
	{
		private readonly Regex contNum = new Regex(@"\A(([A-Z]{4}[0-9]{7})|([0-9]{9}))\z", RegexOptions.Compiled | RegexOptions.IgnoreCase);
		private readonly Regex tdNum = new Regex(@"[0-9]{8}[/,\\]{1}[0-9]{6,8}[/,\\]{1}[0-9а-яa-z]{1,10}", RegexOptions.Compiled | RegexOptions.IgnoreCase);
		private readonly Regex tdDateInNum = new Regex(@"[/,\\]{1}([0-9]{6,8})[/,\\]{1}", RegexOptions.Compiled | RegexOptions.IgnoreCase);

		#region Properties
		public Action<string, object[]> LogHandler { get; set; }
		public Action<Exception> ExceptionHandler { get; set; }

		public ErrorManager ErrorManager { get; private set; }

		public IDAService Service { get; set; }

		public XElement Source { get; set; }

		public string CodeOP { get; set; }

		public Guid? ReportID { get; set; }

		public ReportsSummary ReportSummary { get; set; }

		public int TDLoaded
		{
			get { return TDs != null ? TDs.Count : 0; }
		}

		public int TDinReport
		{
			get
			{
				return (Source.Element(ArrTDSerObj.XName) ?? new XElement("a"))
					.Elements(ArrTDSerObj.TBC_TD_SerializableObject.XName)
					.Count();
			}
		}

		public int GSinReport
		{
			get
			{
				return (Source.Element(PI_RwInfo.XName) ?? new XElement("a"))
					.Elements(PI_RwInfo.GShip.XName)
					.Count();
			}
		}

		public int GSLoaded
		{
			get { return GoodsShipments != null ? GoodsShipments.Count : 0; }
		}

		public ICollection<TD> TDs { get; set; }

		public PI_RwInformation_Info PiRwInformationInfo { get; private set; }

		public Train Train
		{
			get
			{
				return PiRwInformationInfo != null ? PiRwInformationInfo.Train : null;
			}
			set
			{
				if (PiRwInformationInfo != null)
					PiRwInformationInfo.Train = value;
			}
		}

		public ICollection<GoodsShipment> GoodsShipments
		{
			get
			{
				return Train != null
					? Train.GoodsShipments
					: null;
			}
			set
			{
				if (Train != null)
				{
					Train.GoodsShipments.Clear();
					Train.GoodsShipments.AddRange(value);
				}
			}
		}

		public Guid PPVID { get; set; }

		public bool IsTDCountCollision
		{
			get { return TDinReport != TDLoaded; }
		}

		#endregion

		private void Log(string strk, params object[] args)
		{
			LogHandler(strk, args);
		}

		private void ThrowException(Exception ex)
		{

		}

		private ValueLine<string, XElement> GetValueLine()
		{
			return new ValueLine<string, XElement>()
				.SetErrorManager(ErrorManager)
				.Getter(s => (string)s ?? "");
		}

		private static DateTime GetSpecDateFromXml(XElement xe)
		{
			return xe.GetOrDefaultDateTime(SqlDateTime.MinValue.Value, new[] { "dd.MM.yyyy hh:mm:ss", "MM/dd/yyyy hh:mm:ss tt", "yyyyMMdd", "ddMMyy" });
		}

		private static DateTime? GetSpecDateNullableFromXml(XElement xe)
		{
			return xe.GetOrDefaultDateTime(new[] { "dd.MM.yyyy hh:mm:ss", "MM/dd/yyyy hh:mm:ss tt", "yyyyMMdd", "ddMMyy" });
		}

		public AISReportLoadSession()
		{
			ErrorManager = new ErrorManager();
			TDMinimumDayInterval = 60;
			TDMaximumDayInterval = 3;
			ExceptionHandler = (ex => { });
			LogHandler = ((s, o) => { });
			// Все добавляемые ошибки будут выводится и в лог
			ErrorManager.AddErrorVisitors.Push(s =>
			{
				if (s.Level > ErrorLevel.Corrected)
					Log(s.Description);
#if DEBUG
				//else
				//    Console.WriteLine(s.Description);
#endif
			}

);
		}

		public AISReportLoadSession(IDAService idas, XElement aisRep, Guid ppvID, Guid? reportID, string codeOP, Action<string, object[]> logHandler, Action<Exception> exceptionHandler)
			:this()
		{
			ExceptionHandler = exceptionHandler ?? ExceptionHandler;
			LogHandler = logHandler ?? LogHandler;
			ReportID = reportID;
			PPVID = ppvID;
			Service = idas;
			CodeOP = codeOP;
			Source = aisRep;
		}

		public void Load()
		{

#if !DEBUG
			try
#else
			// Симулируем длительную обработку
			//Thread.Sleep(10000);
#endif
			{
				// Формируем TD из xml для отправки в базу
				GenTDsFromXml(Source.Element(ArrTDSerObj.XName));

				// Формируем PiRwInformationInfo из xml для отправки в базу
				GenPI_RwInfoFromXml(Source.Element(PI_RwInfo.XName));

				if (TDinReport == 0)
					return;

				// Удаляем из базы дубликаты товарных партий
				RemoveExistsGoodsShipments();

				// Удаляем из базы дубликаты ТД
				RemoveExistsTDs();

				// Согласование списка ТД и товарных партий
				FittingListTDnGS();

				// Создаем сводку по отчёту
				GenReportSummary();

				// Отправляем изменения в базу
				SubmitAISReportToDatabase();
			}
#if !DEBUG
			catch (Exception ex)
			{
				ErrorManager.Add(Error.Get("Внутренняя ошибка сервера ЦО при загрузке товарных партий из отчёта ReportID '{0}' PPVID = {1}. Обратитесь к администратору.".goFormat(ReportID, PPVID), ErrorLevel.Error));
				Log("Ошибка при загрузке товарных партий из отчёта ReportID '{0}' PPVID = {1} !", ReportID, PPVID);
				ExceptionHandler(ex);
			}
#endif
		}

		private void GenReportSummary()
		{
			ReportSummary =
				new ReportsSummary
					{
						ID = Guid.NewGuid(),
						ReportID = ReportID ?? Guid.NewGuid(),
						DateLoad = DateTime.Now,
						GSinReport = GSinReport,
						TDinReport = TDinReport,
						GSLoaded = GSLoaded,
						TDLoaded = TDLoaded,
						ErrorMessageID = ErrorManager.HasErrors
											? Guid.NewGuid()
											: (Guid?)null
					};
		}

		private void FittingListTDnGS()
		{
			TD dubTD;
			var newTD = new List<TD>();
			using (var db = Service.GetContext())
			{
				#region Проверка ТД на идентичность
				foreach (var td in TDs.ToArray().Where(td => !ErrorManager.ContainsAnyError(td)))
					using (ErrorManager.PushObjectRefUsing(td.ID))
#if !DEBUG
						try
#endif
						{
							dubTD = db.TDs.FirstOrDefault(s => s.TdRegNumber == td.TdRegNumber);
							if (dubTD != null && dubTD.GoodsShipmentID != td.GoodsShipmentID)
								ErrorManager.Add(Error.Get(string.Format(
									"Ошибка привязки ТД и товарной партии. В базе уже есть ТД '{0}', привязанная к другой товарной партии (ППВ:{1} ЖДН:{2} ID:{3})",
									td.TdRegNumber, dubTD.GoodsShipment.Train.PPVNumber, dubTD.GoodsShipment.SMGSNumber, dubTD.GoodsShipmentID),
														   ErrorLevel.Error, td));

							if (TDs.Count(s => s.ID == td.ID) > 1)
								foreach (var td1 in TDs.Where(s => s.ID == td.ID))
									using (ErrorManager.PushObjectRefUsing(td1.ID))
										ErrorManager.Add(Error.Get(string.Format(
											"В отчете присутствуют ТД с одинаковым ID '{0}' номер: '{1}'", td1.ID, td1.TdRegNumber), ErrorLevel.Error, td1));

							if (!ErrorManager.ContainsAnyError(td))
							{
								td.Train = Train;
								newTD.Add(td);
							}
						}
#if !DEBUG
						catch (Exception ex)
						{
							TDs.Remove(td);
							ErrorManager.Add(Error.Get("Внутренняя ошибка сервера ЦО при обработке дубликата для ТД {0} (ID:{1}). Обратитесь к администратору.".goFormat(td.TdRegNumber, td.ID), ErrorLevel.Error, td));
							Log("Ошибка обработки ТД {0} (ID:{1}).", td.TdRegNumber, td.ID);
							ExceptionHandler(ex);
						}
#endif
				TDs = newTD;
				#endregion
			}

			#region Проверяем товарные партии на идентичность
			foreach (var g_s in GoodsShipments.ToArray().Where(g_s => !ErrorManager.ContainsAnyError(g_s)))
				using (ErrorManager.PushObjectRefUsing(g_s.ID))
				{
					if (GoodsShipments.Count(s => s.ID == g_s.ID) > 1)
						foreach (var gs1 in GoodsShipments.Where(s => s.ID == g_s.ID))
							using (ErrorManager.PushObjectRefUsing(gs1.ID))
								ErrorManager.Add(Error.Get(string.Format(
									"В отчете присутствуют товарные партии с одинаковым ID '{0}' номер: '{1}'", gs1.ID, gs1.SMGSNumber), ErrorLevel.Error, gs1));

					if (ErrorManager.ContainsAnyError(g_s))
						GoodsShipments.Remove(g_s);
				}
			#endregion

			#region Отклоняем товарные партии, связанные с ошибочными ТД
			var errsTD = ErrorManager
								.Where(s => s.Level >= ErrorLevel.Error)
								.OfType<Error<TD>>()
								.ToArray();

			Error<TD> errTD = null;

			if (GoodsShipments != null)
				foreach (var g_s in GoodsShipments
									.ToArray()
									.Where(gs => (errTD = errsTD.FirstOrDefault(eTD => eTD.Subject.GoodsShipmentID == gs.ID)) != null))
					using (ErrorManager.PushObjectRefUsing(g_s.ID))
					{
						GoodsShipments.Remove(g_s);
						Log("Товарная партия (ID:{0} ЖДН:{1} PPVID:{2}) отклонена из-за ошибки связанной ТД (ID:{3}).", g_s.ID, g_s.SMGSNumber, PPVID, errTD.Subject.ID);
					}
			#endregion

			#region Отклоняем ТД, связанные с ошибочными товарными партиями
			var errsGS = ErrorManager
								.Where(s => s.Level >= ErrorLevel.Error)
								.OfType<Error<GoodsShipment>>()
								.ToArray();

			Error<GoodsShipment> errGS = null;

			if (TDs != null)
				foreach (var t_d in TDs
									.ToArray()
									.Where(td => (errGS = errsGS.FirstOrDefault(eGS => eGS.Subject.ID == td.GoodsShipmentID)) != null))
					using (ErrorManager.PushObjectRefUsing(t_d.ID))
					{
						TDs.Remove(t_d);
						Log("ТД {0} (ID:{1} PPVID:{2}) отклонена из-за ошибки связанной товарной партии (ID:{3}).", t_d.TdRegNumber, t_d.ID, t_d.TrainID, errGS.Subject.ID);

						t_d.Train = null;
					}
			#endregion

		}

		private void RemoveExistsTDs()
		{
#if !DEBUG
			try
#endif
			{
				Guid[] ids = TDs.Select(s => s.ID).ToArray();
				// удаляем дубликаты ТД
				using (var db = Service.GetContext())
				{
					Guid[] dubs = db.TDs
						.Where(s => ids.Contains(s.ID))
						.Select(s => s.ID)
						.ToArray();
					if (dubs.Length > 0)
					{
						db.TDs.DeleteAllOnSubmit(db.TDs.Where(s => dubs.Contains(s.ID)));
						db.SubmitChanges();
						Log("Удалено {0} копий ТД в базе данных.", dubs.Length);
					}
				}
			}
#if !DEBUG
			catch (Exception ex)
			{
				ErrorManager.Add(Error.Get("Внутренняя ошибка сервера ЦО при попытке удаления дубликатов ТД. Обратитесь к администратору.", ErrorLevel.Error));
				Log("Ошибка при удалении дубликатов ТД в базе данных (ReportID:{0} TrainID:{1}).", ReportID, PiRwInformationInfo.Train.ID);
				ExceptionHandler(ex);
			}
#endif
		}

		private void RemoveExistsGoodsShipments()
		{
			Guid[] gss = GoodsShipments != null ? GoodsShipments.Select(s => s.ID).ToArray() : new Guid[0];
			using (var db = Service.GetContext())
			{
				Guid[] dubs = db.GoodsShipments
					.Where(s => gss.Contains(s.ID))
					.Select(s => s.ID)
					.ToArray();

				if (dubs.Length > 0)
				{
					Log("GetDubs() : GoodsShipments is duplicated:");
					foreach (var id in dubs)
					{
						Log("delete GoodsShipment '{0}'", id);
#if !DEBUG
						try
#endif
						{
							db.DeleteGoodsShipment(id);
							db.SubmitChanges();
						}
#if !DEBUG
						catch (Exception ex)
						{
							ErrorManager.Add(Error.Get("Внутренняя ошибка сервера ЦО при попытке удаления дубликатов товарных партий. Обратитесь к администратору.", ErrorLevel.Error));
							Log("Ошибка при удалении предшествующей товарной партии ID '{0}'!".goFormat(id));
							ExceptionHandler(ex);
						}
#endif
					}
					Log("Удалено {0} копий товарных партий в базе данных.", dubs.Length);
				}
			}
		}

		private void SubmitAISReportToDatabase()
		{
			if (!ErrorManager.HasCriticalErrors)
				ForceSubmitAISReportToDatabase();
		}

		private void ForceSubmitAISReportToDatabase()
		{
			using (var db = Service.GetContext())
			{
				db.PI_RwInformation_Infos.InsertOnSubmit(PiRwInformationInfo);
				db.ReportsSummaries.InsertOnSubmit(ReportSummary);
				//db.TDs.InsertAllOnSubmit(TDs);				
#if !DEBUG
				try
#endif
				{
					db.SubmitChanges(ConflictMode.ContinueOnConflict);
					Log("Успешно загружена информация о поезде (ППВ:{0}, PI_RwID:{1}) в базу данных. Товарных партий: {2} из {3}, ТД: {4} из {5}, Ошибок: {6}, Предупреждений: {7}, Автоисправлений: {8}", Train.PPVNumber, PiRwInformationInfo.ID, GSLoaded, GSinReport, TDLoaded, TDinReport, ErrorManager.Count(s => s.Level == ErrorLevel.Error), ErrorManager.Count(s => s.Level == ErrorLevel.Warning), ErrorManager.Count(s => s.Level == ErrorLevel.Corrected));
				}
#if !DEBUG
				catch (Exception ex)
				{
					ErrorManager.Add(Error.Get("Внутренняя ошибка сервера ЦО при попытке сохранения информации о поезде (ReportID:{0} PPVID:{1}). Обратитесь к администратору.".goFormat(ReportID, PPVID), ErrorLevel.Error));
					Log("Ошибка при загрузке информации о поезде (ReportID:{0} ППВ:{1}, PI_RwID:{2}) в базу данных.", ReportID, PiRwInformationInfo.Train.PPVNumber, PiRwInformationInfo.ID);
					ExceptionHandler(ex);
				}
#endif
			}
		}

		#region GetFromXml-методы
		private void GenTDsFromXml(XElement reps)
		{
			if (reps != null)
			{
				#region TD
				TDs = reps
						.Elements(ArrTDSerObj.TBC_TD_SerializableObject.XName)
						.Select(xe => GetTDFromXml(xe))
						.Where(s => s != null)
						.ToList();
				#endregion
			}
		}

		private TD GetTDFromXml(XElement xe)
		{
			Match match;
			DateTime dt = DateTime.MinValue
				, minDate = DateTime.Now.Subtract(TimeSpan.FromDays(60))
				, maxDate = DateTime.Now.AddDays(3);
			bool isCorrect = true;
			var vals = GetValueLine();

			var vald = new ValueLine<DateTime, XElement>()
				.SetErrorManager(ErrorManager);

			var td = new TD
						{
							ID = xe.Element(ArrTDSerObj.TBC_TD_SerializableObject.ID).GetOrDefaultValued<Guid>(),
							CrDate = DateTime.Now,
							Train = Train,
							ControlDate = GetSpecDateNullableFromXml(xe.Element(ArrTDSerObj.TBC_TD_SerializableObject.DeliveryDate)),
							OPCode = CodeOP.GetSubstring(6)
						};
			td.GoodsShipmentID = td.ID;

			using (ErrorManager.PushObjectRefUsing(td.ID))
			{
				// TdRegNumber
				vals.Reset()
					.GetFrom(xe.Element(ArrTDSerObj.TBC_TD_SerializableObject.G07))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => isCorrect = tdNum.IsMatch(s), "номер ТД '{0}' не соответствует формату.".goFormat(vals.Value), ErrorLevel.Error, td)
					.AssignInAnyCase(s => td.TdRegNumber = s.Length > 30 ? s.Substring(0, 30) : s);

				using (ErrorManager.PushDescPrefixUsing("ТД {0}: ".goFormat(td.TdRegNumber)))
				{
					if (!td.ControlDate.HasValue)
						ErrorManager.Add(Error.Get("не указана дата доставки", ErrorLevel.Warning, td));

					match = tdDateInNum.Match(td.TdRegNumber);
					isCorrect = match.Success &&
								DateTime.TryParseExact(match.Groups[1].Value, new[] { "yyyyMMdd", "ddMMyy" },
													   CultureInfo.InvariantCulture, DateTimeStyles.None, out dt);
					if (isCorrect)
					{
						td.TdDate = dt;
						if (!(isCorrect = ((DateTime.Now - dt).TotalDays <= TDMinimumDayInterval && (dt - DateTime.Now).TotalDays <= TDMaximumDayInterval)))
							ErrorManager.Add(Error.Get("дата ТД должна быть в диапазоне {0:dd.MM.yyyy} - {1:dd.MM.yyyy}"
															.goFormat(minDate, maxDate), ErrorLevel.Error, td));
					}
					else
					{
						td.TdDate = SqlDateTime.MinValue.Value;
						ErrorManager.Add(Error.Get("не удалось распознать дату в номере ТД", ErrorLevel.Error, td));
					}
				}
			}
			return ErrorManager.ContainsAnyError(td) ? null : td;
		}

		[Serialize]
		public int TDMinimumDayInterval { get; set; }

		[Serialize]
		public int TDMaximumDayInterval { get; set; }

		private void GenPI_RwInfoFromXml(XElement xe)
		{
			PiRwInformationInfo = new PI_RwInformation_Info
									{
										ID = Guid.NewGuid(),
										DocumentID = xe.Element(PI_RwInfo.DocumentID).GetOrDefaultValued<Guid>(),
										DocumentDate = GetSpecDateNullableFromXml(xe.Element(PI_RwInfo.DocumentDate)),
										PI_URN = xe.Element(PI_RwInfo.PI_URN).GetOrDefaultValued<Guid>(),
									};

			GenTrainFromXml(xe.Element(PI_RwInfo.Train.XName));

			PiRwInformationInfo.Train.GoodsShipments.AddRange(xe
				.Elements(PI_RwInfo.GShip.XName)
				.Select(s => GetGoodShipmentFromXml(s))
				.Where(s => s != null));
		}

		private void GenTrainFromXml(XElement xe)
		{
			var vals = GetValueLine();

			Train = new Train
							{
								ID = Guid.NewGuid(),
								ReportID = ReportID,
								ArrivalDate = GetSpecDateFromXml(xe.Element(PI_RwInfo.Train.ArrivalDate)),
							};

			// PPVNumber
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.Train.PPVNumber))
				.Edit(s => s.Replace(" ", ""))
				.Is(s => s.Length > 0, "Отсутствует номер ППВ", ErrorLevel.Critical, Train)
				.Is(s => s.Length <= 100, "Номер ППВ должен быть не больше 100 символов", ErrorLevel.Corrected, Train)
				.Assign(s => Train.PPVNumber = s, s => Train.PPVNumber = s.Length > 100 ? s.Substring(0, 100) : s);
			//.Assign(s => ErrorManager.AddErrorVisitors
			//    .Push(s1 => s1.Description = "ППВ №{0}: {1}".goFormat(s, s1.Description)));

			// TrainIndex
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.Train.TrainIndex))
				.Edit(s => s.Replace(" ", ""))
				.Is(s => s.Length > 0, "должен быть указан индекс поезда", ErrorLevel.Corrected, Train)
				.Is(s => s.Length <= 20, "индекс поезда должен быть не больше 20 символов", ErrorLevel.Corrected, Train)
				.Assign(s => Train.TrainIndex = s, s => Train.TrainIndex = s.Length > 20 ? s.Substring(0, 20) : s);

			// TrainNumber
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.Train.TrainNumber))
				.Edit(s => s.Replace(" ", ""))
				.Is(s => s.Length > 0, ErrorLevel.Corrected)
				.Is(s => s.Length <= 40, "номер поезда должен быть не больше 40 символов", ErrorLevel.Corrected, Train)
				.Assign(s => Train.TrainNumber = s, s => Train.TrainNumber = s.Length > 40 ? s.Substring(0, 40) : s);

			#region Вагоны
			Train.Wagons.AddRange(xe.Elements(PI_RwInfo.Train.Wagon.XName)
									.Select(s => GetWagonFromXml(s))
				);

			if (Train.Wagons.Count == 0)
			{
				ErrorManager.Add(Error.Get("В информации о поезде отсутстсвуют вагоны! (ReportID:{0} ППВ:{1}) ".goFormat(ReportID, Train.ID), ErrorLevel.Warning));
				Train.Wagons = null;
			}
			#endregion

			if (ErrorManager.ContainsAnyError(Train))
				Train = null;
		}

		private Wagon GetWagonFromXml(XElement xe)
		{
			var vals = GetValueLine();

			var vald = new ValueLine<double, XElement>()
				.SetErrorManager(ErrorManager)
				.Getter(s => Math.Abs(s.GetDouble()));

			var w = new Wagon
						{
							ID = Guid.NewGuid(),
							WagonNumeric = xe.Element(PI_RwInfo.Train.Wagon.WagonNumeric).GetInt(),
						};

			using (ErrorManager.PushDescPrefixUsing("Вагон {0}: ".goFormat(w.WagonNumeric)))
			{
				// WagonNumber
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.Train.Wagon.WagonNumber))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length > 0, "номер должен быть указан", ErrorLevel.Warning, w)
					.Is(s => s.Length == 0 || s.Length == 8, "номер должен содержать 8 цифр", ErrorLevel.Warning, w)
					.AssignInAnyCase(s => w.WagonNumber = s.GetSubstring(8));

				// DepartureStation
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.Train.Wagon.DepStation))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length > 0, "должен быть указан код станции отправления", ErrorLevel.Corrected, w)
					.Is(s => s.Length == 0 || s.Length == 5, "код станции отправления должен содержать 5 цифр", ErrorLevel.Corrected, w)
					.AssignInAnyCase(s => w.DepartureStation = s.GetSubstring(5));

				// DestinationStation
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.Train.Wagon.DestStation))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length > 0, "должен быть указан код станции назначения", ErrorLevel.Corrected, w)
					.Is(s => s.Length == 0 || s.Length == 5, "код станции назначения должен содержать 5 цифр", ErrorLevel.Corrected, w)
					.AssignInAnyCase(s => w.DestinationStation = s.GetSubstring(5));

				w.EmptyIndicator = xe.GetOrDefaultValued<bool>(PI_RwInfo.Train.Wagon.EmptyIndicator);
				w.ReturnedIndicator = xe.GetOrDefaultValued<bool>(PI_RwInfo.Train.Wagon.RetIndicator);

				// GoodsWeightQuantity
				vald.Reset()
					.GetFrom(xe.Element(PI_RwInfo.Train.Wagon.GWeight))
					.Is(s => s >= 0d, ErrorLevel.Corrected)
					.Assign(s => w.GoodsWeightQuantity = s, s => w.GoodsWeightQuantity = 0d);

				// CountryRegistrationCode
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.Train.Wagon.CntryRegCode))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length == 0 || s.Length == 2, ErrorLevel.Corrected)
					.AssignInAnyCase(s => w.CountryRegistrationCode = s.GetSubstring(2));

				// AddInformation
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.Train.Wagon.CntryRegCode))
					.Is(s => s.Length <= 250, ErrorLevel.Corrected)
					.AssignInAnyCase(s => w.AddInformation = s.GetSubstring(250));

				w.WagonContainers.AddRange(xe
					.Elements(PI_RwInfo.Train.Wagon.ContNums)
					.Select(s => GetContainerFromXml(s))
					.Where(s => s != null)
					);
			}
			return ErrorManager.ContainsAnyError(w) ? null : w;
		}

		private WagonContainer GetContainerFromXml(XElement xes)
		{
			var vals = GetValueLine()
				.Getter(s => ((string)s ?? "").Replace(" ", ""));

			var c = new WagonContainer { ID = Guid.NewGuid() };

			// ContainerNumbers
			vals.GetFrom(xes)
				.Edit(s => s.Replace(" ", ""))
				.Is(s => s.Length == 0 || s.Length == 11 || s.Length == 9, "длина номера контейнера должна быть 9 или 11", ErrorLevel.Corrected, c)
				.Is(s => (s.Length != 11 && s.Length != 9) || contNum.IsMatch(s), "номер контейнера должен быть совпадать с маской 'ABCD1234567'", ErrorLevel.Corrected, c)
				.Assign(s => c.ContainerNumbers = s, s => c.ContainerNumbers = "");

			return ErrorManager.ContainsAnyError(c) ? null : c;
		}

		//private XElement GetElementOrAddError(XElement contXe, XName name, string descErr, ErrorLevel level)
		//{
		//    var res = contXe.Element(name);
		//    if (res == null)
		//        ErrorManager.Add(Error.Get(descErr, level));
		//    return res;
		//}

		private XElement GetElementOrAddError<Tclass>(XElement contXe, XName name, string descErr, ErrorLevel level, Tclass subject)
		{
			var res = contXe.Element(name);
			if (res == null)
				ErrorManager.Add(Error.Get(descErr, level, subject));
			return res;
		}

		private GoodsShipment GetGoodShipmentFromXml(XElement xe)
		{
			var vals = GetValueLine();

			Organization org;

			var g = new GoodsShipment
						{
							ID = (Guid)xe.Element(PI_RwInfo.GShip.GdnID)
						};

			// Добавляем ИД товарной партии в следующие ошибки
			using (ErrorManager.PushObjectRefUsing(g.ID))
			// Добавляем в описании ошибки префикс с ID товарной партии	
			using (ErrorManager.PushDescPrefixUsing("Товарная партия {0}: ".goFormat(g.ID)))
			{
				// SMGSNumber
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.SMGSNumber))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length > 0, "отсутствует номер СМГС", ErrorLevel.Error, g)
					.Is(s => s.Length <= 50, "длина номера должна быть не больше 50", ErrorLevel.Corrected, g)
					.AssignInAnyCase(s => g.SMGSNumber = s.GetSubstring(50));

				// Добавляем в описании ошибки префикс с номером накладной
				if (!g.SMGSNumber.IsEmpty())
				{
					ErrorManager.AddErrorVisitors.Pop();
					ErrorManager.AddErrorVisitors.Push(s => s.Description = "ЖДН {0}: {1}".goFormat(g.SMGSNumber, s.Description));
				}

				// DepartureStation
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.DepStation))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length > 0, "отсутствует станция отправления", ErrorLevel.Warning, g)
					.Is(s => s.Length == 0 || s.Length == 5, "длина кода станции отправления должна быть равно 5", ErrorLevel.Corrected, g)
					.AssignInAnyCase(s => g.DepartureStation = s.GetSubstring(5));

				// DestinationStation
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.DestStation))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length > 0, "отсутствует станция назначения", ErrorLevel.Warning, g)
					.Is(s => s.Length == 0 || s.Length == 5, "длина кода станции назначения должна быть равно 5", ErrorLevel.Corrected, g)
					.AssignInAnyCase(s => g.DestinationStation = s.GetSubstring(5));

				// Consignor
				var xecor = GetElementOrAddError(xe, PI_RwInfo.GShip.Consignor.XName,
												 "не найдена информация об отправителе", ErrorLevel.Warning, g);
				if (xecor != null)
					using (ErrorManager.PushDescPrefixUsing("Отправитель: "))
					{
						org = GetOrganizationFromXml(xecor);
						if (org != null)
							g.ConsignorID = org.ID;
						g.Organization = org;
					}

				// Consignee
				var xecee = GetElementOrAddError(xe, PI_RwInfo.GShip.Consignee.XName,
												 "не найдена информация о получателе", ErrorLevel.Warning, g);
				if (xecee != null)
					using (ErrorManager.PushDescPrefixUsing("Получатель: "))
					{
						org = GetOrganizationFromXml(xecee);
						if (org != null)
							g.ConsigneeID = org.ID;
						g.Organization1 = org;
					}

				// GoodsShipmentWagonNumber
				foreach (var item in xe.Elements(PI_RwInfo.GShip.WagonNums))
					vals.Reset()
						.GetFrom(item)
						.Edit(s => s.Replace(" ", ""))
						.Is(s => s.Length == 0 || s.Length == 8, "номер должен вагона содержать 8 цифр", ErrorLevel.Warning, g)
						.AssignInAnyCase(s => g.GoodsShipmentWagonNumbers.Add(
							new GoodsShipmentWagonNumber
								{
									ID = Guid.NewGuid(),
									WagonNumber = s.GetSubstring(8)
								}));

				// Goods
				g.Goods.AddRange(xe
					.Elements(PI_RwInfo.GShip.Goods.XName)
					.Select(s => GetGoodFromXml(s))
					.Where(s => s != null));

				// Documents
				g.Documents.AddRange(xe
					.Elements(PI_RwInfo.GShip.Documents.XName)
					.Select(s1 => GetDocumentFromXml(s1))
					.Where(s => s != null));

				if (!g.GoodsShipmentWagonNumbers.Any() && !g.Goods.SelectMany(s => s.GoodsContainerNumbers).Any())
					ErrorManager.Add(Error.Get("должен быть указан вагон или контейнер", ErrorLevel.Error, g));

				if (!g.Goods.Any())
					ErrorManager.Add(Error.Get("отсутствуют товары", ErrorLevel.Warning, g));
			}

			return ErrorManager.ContainsAnyError(g) ? null : g;
		}

		private Organization GetOrganizationFromXml(XElement xe)
		{
			var vals = GetValueLine();

			var org = new Organization { ID = Guid.NewGuid() };

			var adr = GetAddressFromXml(xe.Element(PI_RwInfo.GShip.Consignor.Address.XName));
			if (adr != null)
				org.Addresses.Add(adr);

			// Наименования организации
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.GShip.Consignor.OrgName))
				.Edit(s => s.Trim())
				.Is(s => s.Length > 0, "отсутствует наименование", ErrorLevel.Corrected, org)
				//.Is(s => s.Length <= 35, "длина наименования не должна превышать 35", ErrorLevel.Warning)
				.Assign(s => org.OrganizationName = s, s => org.OrganizationName = s.Length > 35 ? s.Substring(0, 35) : s);

			vals.Reset()
				.GetFrom(xe.Elements(PI_RwInfo.GShip.Consignor.RFOrgFeat.XName)
						   .Elements(PI_RwInfo.GShip.Consignor.RFOrgFeat.INN).FirstOrDefault())
				.Edit(s => s.Replace(" ", ""))
				.Is(s => s.Length == 0 || (s.Length == 10 || s.Length == 12), "длина ИНН должна быть равна 10 или 12", ErrorLevel.Corrected, org)
					.Assign(s => org.INN = s.Length >= 12 ? s.GetSubstring(12) : s.GetSubstring(10));

			if (adr != null && org.INN.IsEmpty() && (adr.CountryCode ?? "").ToLower() == "ru")
			{
				// ИНН
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.Consignor.INN))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length == 0 || (s.Length == 10 || s.Length == 12), "длина ИНН должна быть равна 10 или 12", ErrorLevel.Corrected, org)
					.Assign(s => org.INN = s.Length >= 12 ? s.GetSubstring(12) : s.GetSubstring(10));

				// ОКПО
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.Consignor.OKPO))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length > 0, "отсутствует ОКПО", ErrorLevel.Corrected, org)
					.Is(s => s.Length == 0 || s.Length == 8, "длина ОКПО должна быть равна 8", ErrorLevel.Corrected, org)
					.AssignInAnyCase(s => org.OKPO = s.GetSubstring(8));
			}

			return ErrorManager.ContainsAnyError(org) ? null : org;
		}

		private Address GetAddressFromXml(XElement xe)
		{
			var vals = GetValueLine();

			Address adr = new Address { ID = Guid.NewGuid() };

			// PostalCode
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.GShip.Consignor.Address.PostalCode))
				.Edit(s => s.Replace(" ", ""))
				.Is(s => s.Length == 0 || s.Length == 50, "длина почтового индекса не должна превышать 50", ErrorLevel.Corrected, adr)
				.AssignInAnyCase(s => adr.PostalCode = s.GetSubstring(50));

			// City
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.GShip.Consignor.Address.City))
				//.Is(s => s.Length <= 35, "Длина наименования города не должна превышать 35", ErrorLevel.Warning, adr)
				.AssignInAnyCase(s => adr.City = s.GetSubstring(35));

			// CountryCode
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.GShip.Consignor.Address.CountryCode))
				.Edit(s => s.Replace(" ", ""))
				.Is(s => s.Length == 0 || s.Length == 2, "длина кода страны должна быть 2", ErrorLevel.Corrected, adr)
				.AssignInAnyCase(s => adr.CountryCode = s.GetSubstring(2));

			// Region
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.GShip.Consignor.Address.Region))
				//.Is(s => s.Length <= 35, "Длина наименования региона/области не должна превышать 35", ErrorLevel.Warning, adr)
				.AssignInAnyCase(s => adr.Region = s.GetSubstring(35));

			// StreetHouse
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.GShip.Consignor.Address.StreetHouse))
				//.Is(s => s.Length <= 35, "Длина внутреннего адреса не должна превышать 35", ErrorLevel.Warning, adr)
				.AssignInAnyCase(s => adr.StreetHouse = s.GetSubstring(35));

			return ErrorManager.ContainsAnyError(adr) ? null : adr;
		}

		private Document GetDocumentFromXml(XElement xe)
		{
			var vals = GetValueLine();

			var doc = new Document { ID = Guid.NewGuid() };

			// PrDocumentName
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.GShip.Documents.PrDocumentName))
				.Is(s => s.Length <= 1024, "длина наименование документа должна быть не больше 1024 символ", ErrorLevel.Corrected)
				.AssignInAnyCase(s => doc.PrDocumentName = s.GetSubstring(1024));

			// PrDocumentNumber
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.GShip.Documents.PrDocumentNumber))
				.Is(s => s.Length <= 50, "длина номера документа должна быть не больше 50 символ", ErrorLevel.Corrected)
				.AssignInAnyCase(s => doc.PrDocumentName = s.GetSubstring(50));

			// PrDocumentType
			vals.Reset()
				.GetFrom(xe.Element(PI_RwInfo.GShip.Documents.PrDocumentType))
				//.Is(s => s.Length <= 50, "длина типа документа должна быть не больше 50 символ", ErrorLevel.Warning)
				.AssignInAnyCase(s => doc.PrDocumentName = s.GetSubstring(50));

			doc.PrDocumentDate = GetSpecDateNullableFromXml(xe.Element(PI_RwInfo.GShip.Documents.PrDocumentDate));

			return ErrorManager.ContainsAnyError(doc) ? null : doc;
		}

		private Good GetGoodFromXml(XElement xe)
		{
			var vals = GetValueLine();

			var vald = new ValueLine<double, XElement>()
				.SetErrorManager(ErrorManager)
				.Getter(s => s.GetDouble());

			var g = new Good
			{
				ID = Guid.NewGuid(),
				GoodsNumeric = xe.Element(PI_RwInfo.GShip.Goods.GoodsNumeric).GetInt(),
			};

			using (ErrorManager.PushDescPrefixUsing("Товар №{0}: ".goFormat(g.GoodsNumeric)))
			{
				// GoodsTNVEDCode
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.Goods.GoodsTNVEDCode))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length == 0 || s.Length == 10 || s.Length == 14, "в коде ТНВЭД должно быть 10 или 14 цифр", ErrorLevel.Corrected, g)
					.Assign(s => g.GoodsTNVEDCode = s,
					// присваивание кода ТНВЭД в случае если он не прошел валидацию
							s => g.GoodsTNVEDCode = s.Length >= 14 ? s.Substring(0, 14) : s.Length > 10
									? s.Substring(0, 10) : s + '0'.Repeat(10 - s.Length));

				// GoodsDescription
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.Goods.GoodsDescription))
					.Is(s => s.Length > 0, "описание товара должно быть НЕ пустым", ErrorLevel.Corrected, g)
					.AssignInAnyCase(s => g.GoodsDescription = s);

				// GrossWeightQuantity				
				vald.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.Goods.GrWeight))
					.Is(s => s >= 0, "масса брутто не может быть отрицательной", ErrorLevel.Corrected, g)
					.Assign(s => g.GrossWeightQuantity = s, s => g.GrossWeightQuantity = 0);

				// NetWeightQuantity
				vald.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.Goods.NetWeight))
					.Is(s => s >= 0, "масса нетто не может быть отрицательной", ErrorLevel.Corrected, g)
					.Assign(s => g.NetWeightQuantity = s, s => g.NetWeightQuantity = 0);

				// OriginCountryCode
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.Goods.OrigCntCode))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length == 0 || s.Length == 2, ErrorLevel.Corrected)//, "код страны происхождения должен быть двухбуквенным")
					.Assign(s => g.OriginCountryCode = s, s => g.OriginCountryCode = s.Length >= 2
																? s.Substring(0, 2)
																: s + '0'.Repeat(2 - s.Length));

				// InvoicedCost
				using (ErrorManager.PushDescPrefixUsing("фактурная стоимость "))
					vald.Reset()
						.GetFrom(xe.Element(PI_RwInfo.GShip.Goods.InvoicedCost))
						.Is(s => s != 0, "должна быть указана", ErrorLevel.Corrected, g)
						.Is(s => s >= 0, "не может быть отрицательной", ErrorLevel.Corrected, g)
						.Assign(s => g.InvoicedCost = s);

				// CurrencyCode
				using (ErrorManager.PushDescPrefixUsing("код валюты "))
					vals.Reset()
						.GetFrom(xe.Element(PI_RwInfo.GShip.Goods.CurCode))
						.Edit(s => s.Replace(" ", ""))
						.Is(s => s.Length > 0, "должен быть указан", ErrorLevel.Corrected, g)
						.Is(s => s.Length == 0 || s.Length == 3, "должен быть трехбуквенным", ErrorLevel.Corrected, g)
						.Assign(s => g.CurrencyCode = s, s => g.CurrencyCode = s.Length >= 3
															? s.Substring(0, 3)
															: s + '0'.Repeat(3 - s.Length));

				// CurrencyRate
				vald.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.Goods.CurRate))
					.Is(s => s >= 0, "курс валюты не может быть отрицательным", ErrorLevel.Corrected, g)
					.Assign(s => g.CurrencyRate = s);

				// GNGCode
				vals.Reset()
					.GetFrom(xe.Element(PI_RwInfo.GShip.Goods.GNGCode))
					.Edit(s => s.Replace(" ", ""))
					.Is(s => s.Length == 0 || s.Length == 10, "в коде ГНГ должно быть 10 цифр", ErrorLevel.Corrected, g)
					.Assign(s => g.GNGCode = s, s => g.GNGCode = s.Length >= 10
																? s.Substring(0, 10)
																: s + '0'.Repeat(10 - s.Length));

				g.GoodsContainerNumbers.AddRange(
					xe.Elements(PI_RwInfo.GShip.Goods.ContNums)
						.Select(s => GetGoodsContainerNumberXml(s))
						.Where(s => s != null)
					);

				g.GoodsMeasureQuantities.AddRange(
					xe.Elements(PI_RwInfo.GShip.Goods.GoodsMeQuan.XName)
						.Select(s2 => GetGoodsMeasureQuantityFromXml(s2))
						.Where(s => s != null)
					);

				g.GoodsPackagings.AddRange(
					xe.Elements(PI_RwInfo.GShip.Goods.GoodsPack.XName)
						.Select(s2 => GetGoodsPackagingFromXml(s2))
						.Where(s => s != null)
					);
			}
			return ErrorManager.ContainsAnyError(g) ? null : g;
		}

		private GoodsPackaging GetGoodsPackagingFromXml(XElement s2)
		{
			var vals = GetValueLine();

			var gp = new GoodsPackaging
						{
							ID = Guid.NewGuid(),
							PackageQuantity = s2.Element(PI_RwInfo.GShip.Goods.GoodsPack.PackQy)
												.GetInt(),
						};

			// CargoCode
			vals.Reset()
				.GetFrom(s2.Element(PI_RwInfo.GShip.Goods.GoodsPack.CargoCode))
				.Edit(s => s.Replace(" ", ""))
				.Is(s => s.Length == 0 || s.Length == 2, "код типа/класса груза должен быть 2-буквенным", ErrorLevel.Corrected, gp)
				.Assign(s => gp.CargoCode = s, s => gp.CargoCode = "NA");

			// CargoDescription
			vals.Reset()
				.GetFrom(s2.Element(PI_RwInfo.GShip.Goods.GoodsPack.CargoDesc))
				.Is(s => s.Length <= 250, "длина описания товара должна быть не больше 250 символов", ErrorLevel.Corrected, gp)
				.Assign(s => gp.CargoDescription = s, s => gp.CargoDescription = s.Substring(0, 250));

			gp.PackingInformations.AddRange(
				s2.Elements(PI_RwInfo.GShip.Goods.GoodsPack.PackInfo.XName)
					.Select(s3 => GetPackingInformationFromXml(s3))
				);

			return ErrorManager.ContainsAnyError(gp) ? null : gp;
		}

		private PackingInformation GetPackingInformationFromXml(XElement s3)
		{
			var vals = GetValueLine();

			var pi = new PackingInformation
					{
						ID = Guid.NewGuid(),
						PackingQuantity = s3.Element(PI_RwInfo.GShip.Goods.GoodsPack.PackInfo.PakingQuantity).GetInt(),
					};

			// PackingCode
			vals.Reset()
				.GetFrom(s3.Element(PI_RwInfo.GShip.Goods.GoodsPack.PackInfo.PackingCode))
				.Edit(s => s.Replace(" ", ""))
				.Is(s => s.Length == 0 || s.Length == 2, "код упаковки должен быть 2-буквенным", ErrorLevel.Corrected, pi)
				.Assign(s => pi.PackingCode = s, s => pi.PackingCode = "NA");

			// PackingMark
			vals.Reset()
				.GetFrom(s3.Element(PI_RwInfo.GShip.Goods.GoodsPack.PackInfo.PackingMark))
				.Is(s => s.Length <= 250, ErrorLevel.Corrected)
				.Assign(s => pi.PackingMark = s, s => pi.PackingMark = s.Substring(0, 250));

			return ErrorManager.ContainsAnyError(pi) ? null : pi;
		}

		private GoodsMeasureQuantity GetGoodsMeasureQuantityFromXml(XElement s2)
		{
			var vals = GetValueLine();

			var gmq = new GoodsMeasureQuantity
					{
						ID = Guid.NewGuid(),
						GoodsQuantity = s2.Element(PI_RwInfo.GShip.Goods.GoodsMeQuan.GoodsQuantity).GetDouble(),
					};

			// MeasureUnitQualifierCode
			vals.Reset()
				.GetFrom(s2.Element(PI_RwInfo.GShip.Goods.GoodsMeQuan.MeasureUnitQualifierCode))
				.Edit(s => s.Replace(" ", ""))
				.Is(s => s.Length == 0 || s.Length == 3, ErrorLevel.Corrected)
				.Assign(s => gmq.MeasureUnitQualifierCode = s, s => gmq.MeasureUnitQualifierCode = s.GetSubstring(3));

			// MeasureUnitQualifierName
			vals.Reset()
				.GetFrom(s2.Element(PI_RwInfo.GShip.Goods.GoodsMeQuan.MeasureUnitQualifierName))
				.Is(s => s.Length <= 13, ErrorLevel.Corrected)
				.Assign(s => gmq.MeasureUnitQualifierName = s, s => gmq.MeasureUnitQualifierName = s.GetSubstring(13));

			return gmq;
		}

		private GoodsContainerNumber GetGoodsContainerNumberXml(XElement s2)
		{
			var vals = GetValueLine();

			var gcn = new GoodsContainerNumber { ID = Guid.NewGuid(), };

			// ContainerNumber
			vals.Reset()
				.GetFrom(s2)
				.Edit(s => s.Replace(" ", ""))
				.Is(s => s.Length == 0 || s.Length == 11 || s.Length == 9, "длина номера контейнера должна быть 11", ErrorLevel.Corrected, gcn)
				.Is(s => (s.Length != 11 && s.Length != 9) || contNum.IsMatch(s), "номер контейнера должен быть совпадать с маской 'ABCD1234567'", ErrorLevel.Corrected, gcn)
				.Assign(s => gcn.ContainerNumber = s, s => gcn.ContainerNumber = s.Length > 17
																? s.Substring(0, 17)
																: s);
			return ErrorManager.ContainsAnyError(gcn) ? null : gcn;
		}

		#endregion

		#region Xml-константы
		// ReSharper disable MemberCanBePrivate.Global
		public static readonly XNamespace XnsCatPiRu = XNamespace.None;//= XNamespace.Get("urn:customs.ru:Information:PriorInformation:PriorCommonAggregateTypes:4.1.1");
		public static readonly XNamespace XnsCltPiRu = XNamespace.None;//= XNamespace.Get("urn:customs.ru:Information:PriorInformation:PriorCommonLeafTypes:4.1.1");
		public static readonly XNamespace XnsCatRu = XNamespace.None;//= XNamespace.Get("urn:customs.ru:CommonAggregateTypes:4.1.1");
		public static readonly XNamespace XnsCltRu = XNamespace.None;//= XNamespace.Get("urn:customs.ru:CommonLeafTypes:4.1.1");
		public static readonly XNamespace XnsMain = XNamespace.None;//= XNamespace.Get("urn:customs.ru:Information:PriorInformation:PIRailwayTransportedGoods:4.1.1");
		// ReSharper restore MemberCanBePrivate.Global
		private struct PI_RwInfo
		{
#pragma warning disable 169
			// ReSharper disable MemberHidesStaticFromOuterClass
			// ReSharper disable MemberCanBePrivate.Local
			// ReSharper restore MemberCanBePrivate.Local
			public static readonly XName XName = XnsMain + "PI_RwInformation";
			public static readonly XName DocumentID = XnsCatRu + "DocumentID";
			public static readonly XName DocumentDate = XnsCatRu + "DocumentDate";
			public static readonly XName PI_URN = XnsMain + "PI_URN";
			public struct Train
			{
				public static readonly XName XName = XnsMain + "Train";
				public static readonly XName TrainNumber = XnsMain + "TrainNumber";
				public static readonly XName TrainIndex = XnsMain + "TrainIndex";
				public static readonly XName PPVNumber = XnsMain + "PPVNumber";
				public static readonly XName ArrivalDate = XnsMain + "ArrivalDate";
				//public static readonly XName  = XnsMain + "";
				public struct Wagon
				{
					public static readonly XName XName = XnsMain + "Wagon";
					public static readonly XName WagonNumber = XnsMain + "WagonNumber";
					public static readonly XName CntryRegCode = XnsMain + "CountryRegistrationCode";
					public static readonly XName DepStation = XnsMain + "DepartureStation";
					public static readonly XName DestStation = XnsMain + "DestinationStation";
					public static readonly XName GWeight = XnsMain + "GoodsWeightQuantity";
					public static readonly XName EmptyIndicator = XnsMain + "EmptyIndicator";
					public static readonly XName ContNums = XnsMain + "ContainerNumbers";
					public static readonly XName AddInfo = XnsMain + "AddInformaition";
					public static readonly XName WagonNumeric = XnsMain + "WagonNumeric";
					public static readonly XName RetIndicator = XnsMain + "ReturnedIndicator";
				}
			}

			public struct GShip
			{
				public static readonly XName XName = XnsMain + "GoodsShipment";
				public static readonly XName WagonNums = XnsMain + "WagonNumbers";
				public static readonly XName DepStation = XnsMain + "DepartureStation";
				public static readonly XName DestStation = XnsMain + "DestinationStation";
				public static readonly XName ContIndr = XnsMain + "ContainerIndicator";
				public static readonly XName SMGSNumber = XnsMain + "SMGSNumber";
				public class Consignor
				{
					public static readonly XName XName = XnsMain + "Consignor";
					public static readonly XName OrgName = XnsCatRu + "OrganizationName";
					public static readonly XName INN = XnsCatRu + "INN";
					public static readonly XName OKPO = XnsCatRu + "OKPO";
					public struct RFOrgFeat
					{
						public static readonly XName XName = XnsCatRu + "RFOrganizationFeatures";
						public static readonly XName OGRN = XnsCltRu + "OGRN";
						public static readonly XName INN = XnsCltRu + "INN";
						public static readonly XName KPP = XnsCltRu + "KPP";
					}
					public struct Address
					{
						public static readonly XName XName = XnsCatPiRu + "Address";
						public static readonly XName PostalCode = XnsCatRu + "PostalCode";
						public static readonly XName CountryCode = XnsCatRu + "CountryCode";
						public static readonly XName CounryName = XnsCatRu + "CounryName";
						public static readonly XName City = XnsCatRu + "City";
						public static readonly XName StreetHouse = XnsCatRu + "StreetHouse";
						public static readonly XName Region = XnsCatRu + "Region";
					}
				}
				public class Consignee : Consignor
				{
					public static new readonly XName XName = XnsMain + "Consignee";
				}
				public static readonly XName GdnID = XnsMain + "GdnID";
				public struct Goods
				{
					public static readonly XName XName = XnsMain + "Goods";
					public static readonly XName GoodsNumeric = XnsCatPiRu + "GoodsNumeric";
					public static readonly XName GoodsTNVEDCode = XnsCatPiRu + "GoodsTNVEDCode";
					public static readonly XName GoodsDescription = XnsCatPiRu + "GoodsDescription";
					public static readonly XName GrWeight = XnsCatPiRu + "GrossWeightQuantity";
					public static readonly XName NetWeight = XnsCatPiRu + "NetWeightQuantity";
					public static readonly XName ContNums = XnsCatPiRu + "ContainerNumbers";
					public static readonly XName OrigCntCode = XnsCatPiRu + "OriginCountryCode";
					public static readonly XName InvoicedCost = XnsCatPiRu + "InvoicedCost";
					public static readonly XName CurCode = XnsCatPiRu + "CurrencyCode";
					public static readonly XName CurRate = XnsCatPiRu + "CurrencyRate";
					public struct GoodsMeQuan
					{
						public static readonly XName XName = XnsCatPiRu + "GoodsMeasureQuantity";
						public static readonly XName GoodsQuantity = XnsCatPiRu + "GoodsQuantity";
						public static readonly XName MeasureUnitQualifierName = XnsCatPiRu + "MeasureUnitQualifierName";
						public static readonly XName MeasureUnitQualifierCode = XnsCatPiRu + "MeasureUnitQualifierCode";
					}
					public struct GoodsPack
					{
						public static readonly XName XName = XnsCatPiRu + "GoodsPackaging";
						public static readonly XName PackQy = XnsCatPiRu + "PakageQuantity";
						public static readonly XName CargoCode = XnsCatPiRu + "CargoCode";
						public static readonly XName CargoDesc = XnsCatPiRu + "CargoDescription";
						public struct PackInfo
						{
							public static readonly XName XName = XnsCatPiRu + "PackingInformation";
							public static readonly XName PackingCode = XnsCatPiRu + "PackingCode";
							public static readonly XName PakingQuantity = XnsCatPiRu + "PakingQuantity";
							public static readonly XName PackingMark = XnsCatPiRu + "PackingMark";
						}
					}
					public static readonly XName GNGCode = XnsMain + "GNGCode";
				}
				public struct Documents
				{
					public static readonly XName XName = XnsMain + "Documents";
					public static readonly XName PrDocumentName = XnsCatRu + "PrDocumentName";
					public static readonly XName PrDocumentNumber = XnsCatRu + "PrDocumentNumber";
					public static readonly XName PrDocumentDate = XnsCatRu + "PrDocumentDate";
					public static readonly XName PrDocumentType = XnsCatRu + "PrDocumentType";
				}
			}


#pragma warning restore 169
			// ReSharper restore MemberHidesStaticFromOuterClass
		}

		private struct ArrTDSerObj
		{
			public static readonly XName XName = XnsMain + "ArrayOfTBC_TD_SerializableObject";
			public struct TBC_TD_SerializableObject
			{
				public static readonly XName XName = XnsMain + "TBC_TD_SerializableObject";
				public static readonly XName ID = XnsMain + "ID";
				public static readonly XName G07 = XnsMain + "G07";
				public static readonly XName DeliveryDate = XnsMain + "DeliveryDate";
			}
		}
		#endregion
	}
}
